diff --git a/Gruntfile.js b/Gruntfile.js
index b3c29c353348a9e59b0e7c187ff30268d465bde8..84e3f9bb24eb117173f81f9e32c5d2bf3ee1b809 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -70,7 +70,8 @@ module.exports = function (grunt) {
           'js/dist/collapse.js'  : 'js/src/collapse.js',
           'js/dist/dropdown.js'  : 'js/src/dropdown.js',
           'js/dist/modal.js'     : 'js/src/modal.js',
-          'js/dist/scrollspy.js' : 'js/src/scrollspy.js'
+          'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
+          'js/dist/tab.js'       : 'js/src/tab.js'
         }
       }
     },
diff --git a/js/dist/scrollspy.js b/js/dist/scrollspy.js
index a0d774f438b9468c79667062563b9417ee489b53..7cacbab8b9b31059a080a357438676953ddfc854 100644
--- a/js/dist/scrollspy.js
+++ b/js/dist/scrollspy.js
@@ -23,7 +23,6 @@ var ScrollSpy = (function ($) {
   var VERSION = '4.0.0';
   var DATA_KEY = 'bs.scrollspy';
   var JQUERY_NO_CONFLICT = $.fn[NAME];
-  var TRANSITION_DURATION = 150;
 
   var Defaults = {
     offset: 10
diff --git a/js/dist/scrollspy.js.map b/js/dist/scrollspy.js.map
index ba686262fc673f2f9f97afea8a501eb0480ca028..dcd3cf1b02136d5e850296e9d0ccef9ec6024b89 100644
Binary files a/js/dist/scrollspy.js.map and b/js/dist/scrollspy.js.map differ
diff --git a/js/dist/tab.js b/js/dist/tab.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c71d01714fe4d58de29d791ecfc5cdfa25bbb68
--- /dev/null
+++ b/js/dist/tab.js
@@ -0,0 +1,270 @@
+'use strict';
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.0.0): tab.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+var Tab = (function ($) {
+
+  /**
+   * ------------------------------------------------------------------------
+   * Constants
+   * ------------------------------------------------------------------------
+   */
+
+  var NAME = 'tab';
+  var VERSION = '4.0.0';
+  var DATA_KEY = 'bs.tab';
+  var JQUERY_NO_CONFLICT = $.fn[NAME];
+  var TRANSITION_DURATION = 150;
+
+  var Event = {
+    HIDE: 'hide.bs.tab',
+    HIDDEN: 'hidden.bs.tab',
+    SHOW: 'show.bs.tab',
+    SHOWN: 'shown.bs.tab',
+    CLICK: 'click.bs.tab.data-api'
+  };
+
+  var ClassName = {
+    DROPDOWN_MENU: 'dropdown-menu',
+    ACTIVE: 'active',
+    FADE: 'fade',
+    IN: 'in'
+  };
+
+  var Selector = {
+    A: 'a',
+    LI: 'li',
+    LI_DROPDOWN: 'li.dropdown',
+    UL: 'ul:not(.dropdown-menu)',
+    FADE_CHILD: '> .fade',
+    ACTIVE: '.active',
+    ACTIVE_CHILD: '> .active',
+    DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"]',
+    DROPDOWN_ACTIVE_CHILD: '> .dropdown-menu > .active'
+  };
+
+  /**
+   * ------------------------------------------------------------------------
+   * Class Definition
+   * ------------------------------------------------------------------------
+   */
+
+  var Tab = (function () {
+    function Tab(element) {
+      _classCallCheck(this, Tab);
+
+      this._element = element;
+    }
+
+    _createClass(Tab, [{
+      key: 'show',
+
+      // public
+
+      value: function show() {
+        var _this = this;
+
+        if (this._element.parentNode && this._element.parentNode.nodeType == Node.ELEMENT_NODE && $(this._element).parent().hasClass(ClassName.ACTIVE)) {
+          return;
+        }
+
+        var target = undefined;
+        var previous = undefined;
+        var ulElement = $(this._element).closest(Selector.UL)[0];
+        var selector = Util.getSelectorFromElement(this._element);
+
+        if (ulElement) {
+          previous = $.makeArray($(ulElement).find(Selector.ACTIVE));
+          previous = previous[previous.length - 1];
+
+          if (previous) {
+            previous = $(previous).find(Selector.A)[0];
+          }
+        }
+
+        var hideEvent = $.Event(Event.HIDE, {
+          relatedTarget: this._element
+        });
+
+        var showEvent = $.Event(Event.SHOW, {
+          relatedTarget: previous
+        });
+
+        if (previous) {
+          $(previous).trigger(hideEvent);
+        }
+
+        $(this._element).trigger(showEvent);
+
+        if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
+          return;
+        }
+
+        if (selector) {
+          target = $(selector)[0];
+        }
+
+        this._activate($(this._element).closest(Selector.LI)[0], ulElement);
+
+        var complete = function complete() {
+          var hiddenEvent = $.Event(Event.HIDDEN, {
+            relatedTarget: _this._element
+          });
+
+          var shownEvent = $.Event(Event.SHOWN, {
+            relatedTarget: previous
+          });
+
+          $(previous).trigger(hiddenEvent);
+          $(_this._element).trigger(shownEvent);
+        };
+
+        if (target) {
+          this._activate(target, target.parentNode, complete);
+        } else {
+          complete();
+        }
+      }
+    }, {
+      key: '_activate',
+
+      // private
+
+      value: function _activate(element, container, callback) {
+        var active = $(container).find(Selector.ACTIVE_CHILD)[0];
+        var isTransitioning = callback && Util.supportsTransitionEnd() && (active && $(active).hasClass(ClassName.FADE) || !!$(container).find(Selector.FADE_CHILD)[0]);
+
+        var complete = this._transitionComplete.bind(this, element, active, isTransitioning, callback);
+
+        if (active && isTransitioning) {
+          $(active).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
+        } else {
+          complete();
+        }
+
+        if (active) {
+          $(active).removeClass(ClassName.IN);
+        }
+      }
+    }, {
+      key: '_transitionComplete',
+      value: function _transitionComplete(element, active, isTransitioning, callback) {
+        if (active) {
+          $(active).removeClass(ClassName.ACTIVE);
+
+          var dropdownChild = $(active).find(Selector.DROPDOWN_ACTIVE_CHILD)[0];
+          if (dropdownChild) {
+            $(dropdownChild).removeClass(ClassName.ACTIVE);
+          }
+
+          var activeToggle = $(active).find(Selector.DATA_TOGGLE)[0];
+          if (activeToggle) {
+            activeToggle.setAttribute('aria-expanded', false);
+          }
+        }
+
+        $(element).addClass(ClassName.ACTIVE);
+
+        var elementToggle = $(element).find(Selector.DATA_TOGGLE)[0];
+        if (elementToggle) {
+          elementToggle.setAttribute('aria-expanded', true);
+        }
+
+        if (isTransitioning) {
+          Util.reflow(element);
+          $(element).addClass(ClassName.IN);
+        } else {
+          $(element).removeClass(ClassName.FADE);
+        }
+
+        if (element.parentNode && $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
+
+          var dropdownElement = $(element).closest(Selector.LI_DROPDOWN)[0];
+          if (dropdownElement) {
+            $(dropdownElement).addClass(ClassName.ACTIVE);
+          }
+
+          elementToggle = $(element).find(Selector.DATA_TOGGLE)[0];
+          if (elementToggle) {
+            elementToggle.setAttribute('aria-expanded', true);
+          }
+        }
+
+        if (callback) {
+          callback();
+        }
+      }
+    }], [{
+      key: 'VERSION',
+
+      // getters
+
+      get: function () {
+        return VERSION;
+      }
+    }, {
+      key: 'Default',
+      get: function () {
+        return Default;
+      }
+    }, {
+      key: '_jQueryInterface',
+
+      // static
+
+      value: function _jQueryInterface(config) {
+        return this.each(function () {
+          var $this = $(this);
+          var data = $this.data(DATA_KEY);
+
+          if (!data) {
+            data = data = new Tab(this);
+            $this.data(DATA_KEY, data);
+          }
+
+          if (typeof config === 'string') {
+            data[config]();
+          }
+        });
+      }
+    }]);
+
+    return Tab;
+  })();
+
+  /**
+   * ------------------------------------------------------------------------
+   * Data Api implementation
+   * ------------------------------------------------------------------------
+   */
+
+  $(document).on(Event.CLICK, Selector.DATA_TOGGLE, function (event) {
+    event.preventDefault();
+    Tab._jQueryInterface.call($(this), 'show');
+  });
+
+  /**
+   * ------------------------------------------------------------------------
+   * jQuery
+   * ------------------------------------------------------------------------
+   */
+
+  $.fn[NAME] = Tab._jQueryInterface;
+  $.fn[NAME].Constructor = Tab;
+  $.fn[NAME].noConflict = function () {
+    $.fn[NAME] = JQUERY_NO_CONFLICT;
+    return Tab._jQueryInterface;
+  };
+
+  return Tab;
+})(jQuery);
+//# sourceMappingURL=tab.js.map
\ No newline at end of file
diff --git a/js/dist/tab.js.map b/js/dist/tab.js.map
new file mode 100644
index 0000000000000000000000000000000000000000..6981e217a58667076fb68a9be96fcb40bd3fd545
Binary files /dev/null and b/js/dist/tab.js.map differ
diff --git a/js/scrollspy.js b/js/scrollspy.js
deleted file mode 100644
index f860c1abadb18cb798d118bbdada881b2c47002d..0000000000000000000000000000000000000000
--- a/js/scrollspy.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.3.4
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2011-2015 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
-  'use strict';
-
-  // SCROLLSPY CLASS DEFINITION
-  // ==========================
-
-  function ScrollSpy(element, options) {
-    this.$body          = $(document.body)
-    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
-    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
-    this.selector       = (this.options.target || '') + ' .nav li > a'
-    this.offsets        = []
-    this.targets        = []
-    this.activeTarget   = null
-    this.scrollHeight   = 0
-
-    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
-    this.refresh()
-    this.process()
-  }
-
-  ScrollSpy.VERSION  = '3.3.4'
-
-  ScrollSpy.DEFAULTS = {
-    offset: 10
-  }
-
-  ScrollSpy.prototype.getScrollHeight = function () {
-    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
-  }
-
-  ScrollSpy.prototype.refresh = function () {
-    var that          = this
-    var offsetMethod  = 'offset'
-    var offsetBase    = 0
-
-    this.offsets      = []
-    this.targets      = []
-    this.scrollHeight = this.getScrollHeight()
-
-    if (!$.isWindow(this.$scrollElement[0])) {
-      offsetMethod = 'position'
-      offsetBase   = this.$scrollElement.scrollTop()
-    }
-
-    this.$body
-      .find(this.selector)
-      .map(function () {
-        var $el   = $(this)
-        var href  = $el.data('target') || $el.attr('href')
-        var $href = /^#./.test(href) && $(href)
-
-        return ($href
-          && $href.length
-          && $href.is(':visible')
-          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
-      })
-      .sort(function (a, b) { return a[0] - b[0] })
-      .each(function () {
-        that.offsets.push(this[0])
-        that.targets.push(this[1])
-      })
-  }
-
-  ScrollSpy.prototype.process = function () {
-    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
-    var scrollHeight = this.getScrollHeight()
-    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
-    var offsets      = this.offsets
-    var targets      = this.targets
-    var activeTarget = this.activeTarget
-    var i
-
-    if (this.scrollHeight != scrollHeight) {
-      this.refresh()
-    }
-
-    if (scrollTop >= maxScroll) {
-      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
-    }
-
-    if (activeTarget && scrollTop < offsets[0]) {
-      this.activeTarget = null
-      return this.clear()
-    }
-
-    for (i = offsets.length; i--;) {
-      activeTarget != targets[i]
-        && scrollTop >= offsets[i]
-        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
-        && this.activate(targets[i])
-    }
-  }
-
-  ScrollSpy.prototype.activate = function (target) {
-    this.activeTarget = target
-
-    this.clear()
-
-    var selector = this.selector +
-      '[data-target="' + target + '"],' +
-      this.selector + '[href="' + target + '"]'
-
-    var active = $(selector)
-      .parents('li')
-      .addClass('active')
-
-    if (active.parent('.dropdown-menu').length) {
-      active = active
-        .closest('li.dropdown')
-        .addClass('active')
-    }
-
-    active.trigger('activate.bs.scrollspy')
-  }
-
-  ScrollSpy.prototype.clear = function () {
-    $(this.selector)
-      .parentsUntil(this.options.target, '.active')
-      .removeClass('active')
-  }
-
-
-  // SCROLLSPY PLUGIN DEFINITION
-  // ===========================
-
-  function Plugin(option) {
-    return this.each(function () {
-      var $this   = $(this)
-      var data    = $this.data('bs.scrollspy')
-      var options = typeof option == 'object' && option
-
-      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
-      if (typeof option == 'string') data[option]()
-    })
-  }
-
-  var old = $.fn.scrollspy
-
-  $.fn.scrollspy             = Plugin
-  $.fn.scrollspy.Constructor = ScrollSpy
-
-
-  // SCROLLSPY NO CONFLICT
-  // =====================
-
-  $.fn.scrollspy.noConflict = function () {
-    $.fn.scrollspy = old
-    return this
-  }
-
-
-  // SCROLLSPY DATA-API
-  // ==================
-
-  $(window).on('load.bs.scrollspy.data-api', function () {
-    $('[data-spy="scroll"]').each(function () {
-      var $spy = $(this)
-      Plugin.call($spy, $spy.data())
-    })
-  })
-
-}(jQuery);
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index 985da708d02be8172be39e889fe278a4e1fb897c..b66f7bb886510052e6d5a3f6073e5c7024b1d453 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -17,11 +17,10 @@ const ScrollSpy = (($) => {
    * ------------------------------------------------------------------------
    */
 
-  const NAME                = 'scrollspy'
-  const VERSION             = '4.0.0'
-  const DATA_KEY            = 'bs.scrollspy'
-  const JQUERY_NO_CONFLICT  = $.fn[NAME]
-  const TRANSITION_DURATION = 150
+  const NAME               = 'scrollspy'
+  const VERSION            = '4.0.0'
+  const DATA_KEY           = 'bs.scrollspy'
+  const JQUERY_NO_CONFLICT = $.fn[NAME]
 
   const Defaults = {
     offset : 10
diff --git a/js/src/tab.js b/js/src/tab.js
new file mode 100644
index 0000000000000000000000000000000000000000..4668ff9e694fbb6552db06cd1bd79ed278b3c943
--- /dev/null
+++ b/js/src/tab.js
@@ -0,0 +1,278 @@
+import Util from './util'
+
+
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.0.0): tab.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+const Tab = (($) => {
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Constants
+   * ------------------------------------------------------------------------
+   */
+
+  const NAME                = 'tab'
+  const VERSION             = '4.0.0'
+  const DATA_KEY            = 'bs.tab'
+  const JQUERY_NO_CONFLICT  = $.fn[NAME]
+  const TRANSITION_DURATION = 150
+
+  const Event = {
+    HIDE   : 'hide.bs.tab',
+    HIDDEN : 'hidden.bs.tab',
+    SHOW   : 'show.bs.tab',
+    SHOWN  : 'shown.bs.tab',
+    CLICK  : 'click.bs.tab.data-api'
+  }
+
+  const ClassName = {
+    DROPDOWN_MENU : 'dropdown-menu',
+    ACTIVE        : 'active',
+    FADE          : 'fade',
+    IN            : 'in'
+  }
+
+  const Selector = {
+    A                     : 'a',
+    LI                    : 'li',
+    LI_DROPDOWN           : 'li.dropdown',
+    UL                    : 'ul:not(.dropdown-menu)',
+    FADE_CHILD            : '> .fade',
+    ACTIVE                : '.active',
+    ACTIVE_CHILD          : '> .active',
+    DATA_TOGGLE           : '[data-toggle="tab"], [data-toggle="pill"]',
+    DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu > .active'
+  }
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Class Definition
+   * ------------------------------------------------------------------------
+   */
+
+  class Tab {
+
+    constructor(element) {
+      this._element = element
+    }
+
+
+    // getters
+
+    static get VERSION() {
+      return VERSION
+    }
+
+    static get Default() {
+      return Default
+    }
+
+
+    // public
+
+    show() {
+      if (this._element.parentNode &&
+         (this._element.parentNode.nodeType == Node.ELEMENT_NODE) &&
+         ($(this._element).parent().hasClass(ClassName.ACTIVE))) {
+        return
+      }
+
+      let target
+      let previous
+      let ulElement = $(this._element).closest(Selector.UL)[0]
+      let selector  = Util.getSelectorFromElement(this._element)
+
+      if (ulElement) {
+        previous = $.makeArray($(ulElement).find(Selector.ACTIVE))
+        previous = previous[previous.length - 1]
+
+        if (previous) {
+          previous = $(previous).find(Selector.A)[0]
+        }
+      }
+
+      let hideEvent = $.Event(Event.HIDE, {
+        relatedTarget: this._element
+      })
+
+      let showEvent = $.Event(Event.SHOW, {
+        relatedTarget: previous
+      })
+
+      if (previous) {
+        $(previous).trigger(hideEvent)
+      }
+
+      $(this._element).trigger(showEvent)
+
+      if (showEvent.isDefaultPrevented() ||
+         (hideEvent.isDefaultPrevented())) {
+        return
+      }
+
+      if (selector) {
+        target = $(selector)[0]
+      }
+
+      this._activate(
+        $(this._element).closest(Selector.LI)[0],
+        ulElement
+      )
+
+      let complete = () => {
+        let hiddenEvent = $.Event(Event.HIDDEN, {
+          relatedTarget: this._element
+        })
+
+        let shownEvent  = $.Event(Event.SHOWN, {
+          relatedTarget: previous
+        })
+
+        $(previous).trigger(hiddenEvent)
+        $(this._element).trigger(shownEvent)
+      }
+
+      if (target) {
+        this._activate(target, target.parentNode, complete)
+      } else {
+        complete()
+      }
+    }
+
+
+    // private
+
+    _activate(element, container, callback) {
+      let active          = $(container).find(Selector.ACTIVE_CHILD)[0]
+      let isTransitioning = callback
+        && Util.supportsTransitionEnd()
+        && ((active && $(active).hasClass(ClassName.FADE))
+           || !!$(container).find(Selector.FADE_CHILD)[0])
+
+      let complete = this._transitionComplete.bind(
+        this, element, active, isTransitioning, callback)
+
+      if (active && isTransitioning) {
+        $(active)
+          .one(Util.TRANSITION_END, complete)
+          .emulateTransitionEnd(TRANSITION_DURATION)
+
+      } else {
+        complete()
+      }
+
+      if (active) {
+        $(active).removeClass(ClassName.IN)
+      }
+    }
+
+    _transitionComplete(element, active, isTransitioning, callback) {
+      if (active) {
+        $(active).removeClass(ClassName.ACTIVE)
+
+        let dropdownChild = $(active).find(
+          Selector.DROPDOWN_ACTIVE_CHILD
+        )[0]
+        if (dropdownChild) {
+          $(dropdownChild).removeClass(ClassName.ACTIVE)
+        }
+
+        let activeToggle = $(active).find(Selector.DATA_TOGGLE)[0]
+        if (activeToggle) {
+          activeToggle.setAttribute('aria-expanded', false)
+        }
+      }
+
+      $(element).addClass(ClassName.ACTIVE)
+
+      let elementToggle = $(element).find(Selector.DATA_TOGGLE)[0]
+      if (elementToggle) {
+        elementToggle.setAttribute('aria-expanded', true)
+      }
+
+      if (isTransitioning) {
+        Util.reflow(element)
+        $(element).addClass(ClassName.IN)
+      } else {
+        $(element).removeClass(ClassName.FADE)
+      }
+
+      if (element.parentNode &&
+         ($(element.parentNode).hasClass(ClassName.DROPDOWN_MENU))) {
+
+        let dropdownElement = $(element).closest(Selector.LI_DROPDOWN)[0]
+        if (dropdownElement) {
+          $(dropdownElement).addClass(ClassName.ACTIVE)
+        }
+
+        elementToggle = $(element).find(Selector.DATA_TOGGLE)[0]
+        if (elementToggle) {
+          elementToggle.setAttribute('aria-expanded', true)
+        }
+      }
+
+      if (callback) {
+        callback()
+      }
+    }
+
+
+    // static
+
+    static _jQueryInterface(config) {
+      return this.each(function () {
+        let $this = $(this)
+        let data  = $this.data(DATA_KEY)
+
+        if (!data) {
+          data = data = new Tab(this)
+          $this.data(DATA_KEY, data)
+        }
+
+        if (typeof config === 'string') {
+          data[config]()
+        }
+      })
+    }
+
+  }
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * Data Api implementation
+   * ------------------------------------------------------------------------
+   */
+
+  $(document)
+    .on(Event.CLICK, Selector.DATA_TOGGLE, function (event) {
+    event.preventDefault()
+    Tab._jQueryInterface.call($(this), 'show')
+  })
+
+
+  /**
+   * ------------------------------------------------------------------------
+   * jQuery
+   * ------------------------------------------------------------------------
+   */
+
+  $.fn[NAME]             = Tab._jQueryInterface
+  $.fn[NAME].Constructor = Tab
+  $.fn[NAME].noConflict  = function () {
+    $.fn[NAME] = JQUERY_NO_CONFLICT
+    return Tab._jQueryInterface
+  }
+
+  return Tab
+
+})(jQuery)
+
+export default Tab
diff --git a/js/tests/index.html b/js/tests/index.html
index 0262eec2a98fb6fd5f8854f809d61dee3216b978..6f325115bb1ce66f2ff49b630feb471be9d7c18f 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -138,9 +138,9 @@
     <script src="../../js/dist/dropdown.js"></script>
     <script src="../../js/dist/modal.js"></script>
     <script src="../../js/dist/scrollspy.js"></script>
+    <script src="../../js/dist/tab.js"></script>
 
     <!-- Old Plugin sources -->
-    <script src="../../js/tab.js"></script>
     <script src="../../js/tooltip.js"></script>
     <script src="../../js/popover.js"></script>
 
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index a3fed1e9faa3a02238988246f9588cdbbbf01b63..ff6685fedec148e04ffd946ec1d9f34b700fe54f 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -35,10 +35,10 @@
   <h4>Tabs without fade</h4>
 
   <ul id="myTab" class="nav nav-tabs">
-    <li class="active"><a href="#home" data-toggle="tab">Home</a></li>
-    <li><a href="#profile" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop1" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="active nav-item"><a href="#home" class="nav-link" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a href="#profile" data-toggle="tab" class="nav-link">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop1" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop1">
         <li><a href="#dropdown1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -67,10 +67,10 @@
   <h4>Tabs with fade</h4>
 
   <ul id="myTab1" class="nav nav-tabs">
-    <li class="active"><a href="#home1" data-toggle="tab">Home</a></li>
-    <li><a href="#profile1" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop2" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="active nav-item"><a class="nav-link" href="#home1" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile1" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop2" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop2">
         <li><a href="#dropdown1-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown1-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -99,10 +99,10 @@
   <h4>Tabs without fade (no initially active pane)</h4>
 
   <ul id="myTab2" class="nav nav-tabs">
-    <li><a href="#home2" data-toggle="tab">Home</a></li>
-    <li><a href="#profile2" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop3" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="nav-item"><a class="nav-link" href="#home2" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile2" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop3" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop3">
         <li><a href="#dropdown2-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown2-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -131,10 +131,10 @@
   <h4>Tabs with fade (no initially active pane)</h4>
 
   <ul id="myTab3" class="nav nav-tabs">
-    <li><a href="#home3" data-toggle="tab">Home</a></li>
-    <li><a href="#profile3" data-toggle="tab">Profile</a></li>
-    <li class="dropdown">
-      <a href="#" id="myTabDrop4" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
+    <li class="nav-item"><a class="nav-link" href="#home3" data-toggle="tab">Home</a></li>
+    <li class="nav-item"><a class="nav-link" href="#profile3" data-toggle="tab">Profile</a></li>
+    <li class="dropdown nav-item">
+      <a href="#" id="myTabDrop4" class="dropdown-toggle nav-link" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
       <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop4">
         <li><a href="#dropdown3-1" tabindex="-1" data-toggle="tab">@fat</a></li>
         <li><a href="#dropdown3-2" tabindex="-1" data-toggle="tab">@mdo</a></li>
@@ -164,9 +164,9 @@
 
 <!-- JavaScript Includes -->
 <script src="../vendor/jquery.min.js"></script>
-<script src="../../transition.js"></script>
-<script src="../../tab.js"></script>
-<script src="../../dropdown.js"></script>
+<script src="../../dist/util.js"></script>
+<script src="../../dist/tab.js"></script>
+<script src="../../dist/dropdown.js"></script>
 
 </body>
 </html>