diff --git a/app/assets/images/feature_highlight/graphic-boards.png b/app/assets/images/feature_highlight/graphic-boards.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0550a383746036811565d58329dbeb9f75a3750
Binary files /dev/null and b/app/assets/images/feature_highlight/graphic-boards.png differ
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight.js b/app/assets/javascripts/feature_highlight/feature_highlight.js
new file mode 100644
index 0000000000000000000000000000000000000000..f65cb3211a609e3d3d6f859e13684e68322aa356
--- /dev/null
+++ b/app/assets/javascripts/feature_highlight/feature_highlight.js
@@ -0,0 +1,44 @@
+import Cookies from 'js-cookie';
+
+export const getCookieName = cookieId => `feature-highlighted-${cookieId}`;
+export const getSelector = highlightId => `.js-feature-highlight[data-highlight=${highlightId}]`;
+
+export const showPopover = function showPopover() {
+ this.popover('show');
+ this.addClass('disable-animation');
+};
+
+export const hidePopover = function hidePopover() {
+ this.popover('hide');
+ this.removeClass('disable-animation');
+};
+
+export const dismiss = function dismiss(cookieId) {
+ Cookies.set(getCookieName(cookieId), true);
+ hidePopover.call(this);
+ this.hide();
+};
+
+export const mouseenter = function mouseenter() {
+ const $featureHighlight = $(this);
+ showPopover.call($featureHighlight);
+
+ document.querySelector('.popover')
+ .addEventListener('mouseleave', hidePopover.bind($featureHighlight));
+};
+
+export const mouseleave = function mouseleave() {
+ if (!document.querySelector('.popover:hover')) {
+ const $featureHighlight = $(this);
+ hidePopover.call($featureHighlight);
+ }
+};
+
+export const setupDismissButton = function setupDismissButton() {
+ const popoverId = this.getAttribute('aria-describedby');
+ const cookieId = this.dataset.highlight;
+ const $popover = $(this);
+
+ document.querySelector(`#${popoverId} .dismiss-feature-highlight`)
+ .addEventListener('click', dismiss.bind($popover, cookieId));
+};
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight_manager.js b/app/assets/javascripts/feature_highlight/feature_highlight_manager.js
new file mode 100644
index 0000000000000000000000000000000000000000..d5588d2c52b6c2d8abca538544f47f7cdb8ae221
--- /dev/null
+++ b/app/assets/javascripts/feature_highlight/feature_highlight_manager.js
@@ -0,0 +1,60 @@
+import Cookies from 'js-cookie';
+import {
+ getCookieName,
+ getSelector,
+ hidePopover,
+ setupDismissButton,
+ mouseenter,
+ mouseleave,
+} from './feature_highlight';
+
+export default class FeatureHighlightManager {
+ constructor(highlightOrder) {
+ this.highlightOrder = highlightOrder;
+ }
+
+ init() {
+ const featureId = this.highlightOrder.find(FeatureHighlightManager.shouldHighlightFeature);
+
+ if (featureId) {
+ FeatureHighlightManager.highlightFeature(featureId);
+ window.addEventListener('scroll', () => {
+ const $featureHighlight = $(getSelector(featureId));
+ hidePopover.call($featureHighlight);
+ });
+ }
+ }
+
+ static shouldHighlightFeature(id) {
+ const element = document.querySelector(getSelector(id));
+ const previouslyDismissed = Cookies.get(getCookieName(id)) === 'true';
+
+ return element && !previouslyDismissed;
+ }
+
+ static highlightFeature(id) {
+ const $selector = $(getSelector(id));
+ const $parent = $selector.parent();
+ const $popoverContent = $parent.siblings('.feature-highlight-popover-content');
+
+ // Setup popover
+ $selector.data('content', $popoverContent[0].outerHTML);
+ $selector.popover({
+ html: true,
+ // Override the existing template to add custom CSS classes
+ template: `
+
+ `,
+ });
+
+ $selector.on('mouseenter', mouseenter);
+ $selector.on('mouseleave', mouseleave);
+ $selector.on('inserted.bs.popover', setupDismissButton);
+
+ // Display feature highlight
+ $selector.removeAttr('disabled');
+ }
+}
diff --git a/app/assets/javascripts/feature_highlight/feature_highlight_options.js b/app/assets/javascripts/feature_highlight/feature_highlight_options.js
new file mode 100644
index 0000000000000000000000000000000000000000..51e2eec2165feba1355a5308a66f9b57531a412b
--- /dev/null
+++ b/app/assets/javascripts/feature_highlight/feature_highlight_options.js
@@ -0,0 +1,8 @@
+import FeatureHighlightManager from './feature_highlight_manager';
+
+const highlightOrder = ['issue-boards'];
+
+document.addEventListener('DOMContentLoaded', () => {
+ const featureHighlight = new FeatureHighlightManager(highlightOrder);
+ featureHighlight.init();
+});
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 97ce2803ee6ae36c9a1f36a4baa723bc6e2a9747..e74f2a8eb1c7fa9d49ac80e15882c4f8712ee921 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -102,6 +102,7 @@ import './label_manager';
import './labels';
import './labels_select';
import './layout_nav';
+import './feature_highlight/feature_highlight_options';
import LazyLoader from './lazy_loader';
import './line_highlighter';
import './logo';
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 31eb1c54519b71886dc7e5e8f67f5cd9e6b1dc1d..f16201c9d528162f31619e1455a547b05433d70d 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -51,3 +51,4 @@
@import "framework/snippets";
@import "framework/memory_graph";
@import "framework/responsive-tables";
+@import "framework/feature_highlight";
diff --git a/app/assets/stylesheets/framework/feature_highlight.scss b/app/assets/stylesheets/framework/feature_highlight.scss
new file mode 100644
index 0000000000000000000000000000000000000000..07579a69f1876a73282333f4a1bec7d324359037
--- /dev/null
+++ b/app/assets/stylesheets/framework/feature_highlight.scss
@@ -0,0 +1,102 @@
+.feature-highlight {
+ @include webkit-prefix(animation, pulse-highlight 2s infinite);
+
+ margin-left: 16px;
+ width: 8px;
+ height: 8px;
+ background-color: $blue-500;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 0 0 rgba(31, 120, 209, 0.4);
+
+ &:hover,
+ &.disable-animation {
+ @include webkit-prefix(animation, none);
+ }
+
+ &[disabled] {
+ display: none;
+ }
+}
+
+.is-showing-fly-out {
+ .feature-highlight {
+ display: none;
+ }
+}
+
+.feature-highlight-popover-content {
+ display: none;
+
+ hr {
+ margin: $gl-padding * 0.5 0;
+ }
+
+ // Extend btn-link because it is being overriden by default
+ .btn-link {
+ @extend .btn-link;
+
+ &:hover {
+ @extend .btn-link:hover;
+ }
+
+ &:focus {
+ @extend .btn-link:focus;
+ }
+
+ &:active {
+ @extend .btn-link:active;
+ }
+
+ svg path {
+ fill: currentColor;
+ }
+ }
+
+ .dismiss-feature-highlight {
+ padding: 0;
+ }
+
+ img {
+ width: 100%;
+ padding: 25px 14px 0;
+ background-color: rgb(239, 237, 249);
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ border-bottom: 1px solid #e1e1e1;
+ }
+}
+
+.popover .feature-highlight-popover-content {
+ display: block;
+}
+
+.feature-highlight-popover {
+ padding: 0;
+
+ .popover-content {
+ padding: 0;
+ }
+
+ &.popover > .arrow::after {
+ border-right-color: rgb(239, 237, 249);
+ }
+}
+
+.feature-highlight-popover-sub-content {
+ padding: 9px 14px;
+}
+
+@include keyframes(pulse-highlight) {
+ 0% {
+ @include webkit-prefix(box-shadow, 0 0 0 0 rgba(31, 120, 209, 0.4));
+ }
+
+ 70% {
+ @include webkit-prefix(box-shadow, 0 0 0 10px rgba(31, 120, 209, 0));
+ }
+
+ 100% {
+ @include webkit-prefix(box-shadow, 0 0 0 0 rgba(31, 120, 209, 0));
+ }
+}
diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml
index 115d08230235f82ee30164e744cb699ba23b2271..ec061ff6cdb5a1acd69ce4ad5b4a02eb3ed8bda8 100644
--- a/app/views/layouts/nav/_new_project_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_project_sidebar.html.haml
@@ -97,6 +97,23 @@
= link_to project_boards_path(@project), title: 'Boards' do
%span
Boards
+ .feature-highlight.js-feature-highlight{ disabled: true, data: { trigger: 'manual', container: 'body', toggle: 'popover', animation: 'false', placement: 'right', highlight: 'issue-boards' } }
+ .feature-highlight-popover-content
+ %img{ :alt => "Issue Boards", :src => image_path('feature_highlight/graphic-boards.png') }
+ .feature-highlight-popover-sub-content
+ Use
+ = link_to 'Issue Boards', project_boards_path(@project)
+ to create customized software development workflows like
+ %strong
+ Scrum
+ or
+ %strong
+ Kanban
+ %hr
+ %button.btn.btn-link.dismiss-feature-highlight{ type: 'button'}
+ Got it! Don't show this again
+ = render 'shared/icons/icon_thumbs_up.svg'
+
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: 'Labels' do
diff --git a/app/views/shared/icons/_icon_thumbs_up.svg b/app/views/shared/icons/_icon_thumbs_up.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7267462418eb5334dc1f7b44856dfed818bf8bec
--- /dev/null
+++ b/app/views/shared/icons/_icon_thumbs_up.svg
@@ -0,0 +1 @@
+