diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee
index d540cc4dc467f1802055bc5a32d06267acd4181d..77512d187c9537ec6a767703c02079589df3bd3e 100644
--- a/app/assets/javascripts/gl_form.js.coffee
+++ b/app/assets/javascripts/gl_form.js.coffee
@@ -34,6 +34,8 @@ class @GLForm
       # form and textarea event listeners
       @addEventListeners()
 
+      gl.text.init(@form)
+
     # hide discard button
     @form.find('.js-note-discard').hide()
 
@@ -42,6 +44,7 @@ class @GLForm
   clearEventListeners: ->
     @textarea.off 'focus'
     @textarea.off 'blur'
+    gl.text.removeListeners(@form)
 
   addEventListeners: ->
     @textarea.on 'focus', ->
diff --git a/app/assets/javascripts/lib/text_utility.js.coffee b/app/assets/javascripts/lib/text_utility.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..bb2772dfed2efa36a577e46cb91d3d4961add07f
--- /dev/null
+++ b/app/assets/javascripts/lib/text_utility.js.coffee
@@ -0,0 +1,79 @@
+((w) ->
+  w.gl ?= {}
+  w.gl.text ?= {}
+
+  gl.text.randomString = -> Math.random().toString(36).substring(7)
+
+  gl.text.replaceRange = (s, start, end, substitute) ->
+    s.substring(0, start) + substitute + s.substring(end);
+
+  gl.text.selectedText = (text, textarea) ->
+    text.substring(textarea.selectionStart, textarea.selectionEnd)
+
+  gl.text.insertText = (textArea, text, tag, selected, wrap) ->
+    selectedSplit = selected.split('\n')
+    startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
+
+    if selectedSplit.length > 1 and not wrap
+      insertText = selectedSplit.map((val) ->
+        if val.indexOf(tag) is 0
+          "#{val.replace(tag, '')}"
+        else
+          "#{tag}#{val}"
+      ).join('\n')
+    else
+      insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
+
+    if document.queryCommandSupported('insertText')
+      document.execCommand 'insertText', false, insertText
+    else
+      try
+        document.execCommand("ms-beginUndoUnit")
+
+      textArea.value = @replaceRange(
+          text,
+          textArea.selectionStart,
+          textArea.selectionEnd,
+          insertText)
+      try
+        document.execCommand("ms-endUndoUnit")
+
+    @moveCursor(textArea, tag, wrap)
+
+  gl.text.moveCursor = (textArea, tag, wrapped) ->
+    return unless textArea.setSelectionRange
+
+    if textArea.selectionStart is textArea.selectionEnd
+      if wrapped
+        pos = textArea.selectionStart - tag.length
+      else
+        pos = textArea.selectionStart
+
+      textArea.setSelectionRange pos, pos
+
+  gl.text.updateText = (textArea, tag, wrap) ->
+    $textArea = $(textArea)
+    oldVal = $textArea.val()
+    textArea = $textArea.get(0)
+    text = $textArea.val()
+    selected = @selectedText(text, textArea)
+    $textArea.focus()
+
+    @insertText(textArea, text, tag, selected, wrap)
+
+  gl.text.init = (form) ->
+    self = @
+    $('.js-md', form)
+      .off 'click'
+      .on 'click', ->
+        $this = $(@)
+        self.updateText(
+          $this.closest('.md-area').find('textarea'),
+          $this.data('md-tag'),
+          not $this.data('md-prepend')
+        )
+
+  gl.text.removeListeners = (form) ->
+    $('.js-md', form).off()
+
+) window
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index fd885b38680c0d830d91e66f99c7e08f60302b52..25f9c50258e5ade0d7f4383321c85ca7ed96e2c1 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -65,6 +65,11 @@
     a {
       padding-top: 0;
       line-height: 1;
+      border-bottom: 1px solid $border-color;
+
+      &.btn.btn-xs {
+        padding: 2px 5px;
+      }
     }
   }
 }
@@ -99,3 +104,26 @@
     }
   }
 }
+
+.toolbar-group {
+  float: left;
+  margin-right: -5px;
+  margin-left: $gl-padding;
+
+  &:first-child {
+    margin-left: 0;
+  }
+}
+
+.toolbar-btn {
+  float: left;
+  padding: 0 5px;
+  color: #959494;
+  background: transparent;
+  border: 0;
+  outline: 0;
+
+  &:hover {
+    color: $gl-link-color;
+  }
+}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index de5ccbe2b6ccc0a670b29639b6d75f974d953e72..3784010348a0dcdbfa0876081f16283d08c7197d 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -179,6 +179,10 @@
   border-top: 1px solid $border-color;
 }
 
+.md-helper {
+  padding-top: 10px;
+}
+
 .toolbar-button {
   padding: 0;
   background: none;
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 067a00660aaa6560128d6b1bdaa0156b761c03bd..a0dafc52622eea3afa05b37a8d34d471ef768cdc 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -185,4 +185,17 @@ module GitlabMarkdownHelper
       ''
     end
   end
+
+  def markdown_toolbar_button(options = {})
+    data = options[:data].merge({ container: "body" })
+    content_tag :button,
+      type: "button",
+      class: "toolbar-btn js-md has-tooltip hidden-xs",
+      tabindex: -1,
+      data: data,
+      title: options[:title],
+      aria: { label: options[:title] } do
+      icon(options[:icon])
+    end
+  end
 end
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 28a28282fd3b3c51abaeb9c776ee39be6e4f2ad3..ca6714ef42b18c69f26f2df3d82f2ed0478b8f01 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -14,8 +14,17 @@
           %span This is a confidential issue. Your comment will not be visible to the public.
           
       %li.pull-right
-        %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
-          Go full screen
+        .toolbar-group
+          = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
+          = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
+          = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
+          = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" })
+          = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
+          = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
+          = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
+        .toolbar-group
+          %button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
+            =icon("arrows-alt fw")
 
   .md-write-holder
     = yield
@@ -24,7 +33,7 @@
   - if defined?(referenced_users) && referenced_users
     %div.referenced-users.hide
       %span
-        = icon('exclamation-triangle')
+        = icon("exclamation-triangle")
         You are about to add
         %strong
           %span.js-referenced-users-count 0
diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml
index 0b00204340863dd151e46eb3b6ae986d9d313299..7d1cbc62e86dc83ecfee4471cd42529363fa4f4f 100644
--- a/app/views/projects/notes/_hints.html.haml
+++ b/app/views/projects/notes/_hints.html.haml
@@ -5,4 +5,4 @@
     is supported
   %button.toolbar-button.markdown-selector{ type: 'button', tabindex: '-1' }
     = icon('file-image-o', class: 'toolbar-button-icon')
-    Attach a file
+    Attach a file
\ No newline at end of file
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index c3cb3379440e6e2e38787dde0e9802005d127d0d..5065dfb849cf07d3471d5fe1adaaa81231e0adb7 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -22,7 +22,7 @@ describe 'Issues', feature: true do
 
     before do
       visit edit_namespace_project_issue_path(project.namespace, project, issue)
-      click_button "Go full screen"
+      find('.js-zen-enter').click
     end
 
     it 'should open new issue popup' do
diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee
index ea27f36e9b5131756cc858c0b9df2179792662d3..71f0c1076c5fceddd7c9e9afa3ad0e0196624c39 100644
--- a/spec/javascripts/issue_spec.js.coffee
+++ b/spec/javascripts/issue_spec.js.coffee
@@ -1,3 +1,4 @@
+#= require lib/text_utility
 #= require issue
 
 describe 'Issue', ->
@@ -38,7 +39,7 @@ describe 'reopen/close issue', ->
     expect(typeof $btnClose.prop('disabled')).toBe('undefined')
 
     $btnClose.trigger('click')
-    
+
     expect($btnReopen).toBeVisible()
     expect($btnClose).toBeHidden()
     expect($('div.status-box-closed')).toBeVisible()
@@ -50,7 +51,7 @@ describe 'reopen/close issue', ->
       expect(req.type).toBe('PUT')
       expect(req.url).toBe('http://goesnowhere.nothing/whereami')
       req.success saved: false
-    
+
     $btnClose = $('a.btn-close')
     $btnReopen = $('a.btn-reopen')
     $btnClose.attr('href','http://goesnowhere.nothing/whereami')
@@ -59,7 +60,7 @@ describe 'reopen/close issue', ->
     expect(typeof $btnClose.prop('disabled')).toBe('undefined')
 
     $btnClose.trigger('click')
-    
+
     expect($btnReopen).toBeHidden()
     expect($btnClose).toBeVisible()
     expect($('div.status-box-closed')).toBeHidden()
@@ -73,7 +74,7 @@ describe 'reopen/close issue', ->
       expect(req.type).toBe('PUT')
       expect(req.url).toBe('http://goesnowhere.nothing/whereami')
       req.error()
-    
+
     $btnClose = $('a.btn-close')
     $btnReopen = $('a.btn-reopen')
     $btnClose.attr('href','http://goesnowhere.nothing/whereami')
@@ -82,7 +83,7 @@ describe 'reopen/close issue', ->
     expect(typeof $btnClose.prop('disabled')).toBe('undefined')
 
     $btnClose.trigger('click')
-    
+
     expect($btnReopen).toBeHidden()
     expect($btnClose).toBeVisible()
     expect($('div.status-box-closed')).toBeHidden()
@@ -105,4 +106,4 @@ describe 'reopen/close issue', ->
     expect($btnReopen).toBeHidden()
     expect($btnClose).toBeVisible()
     expect($('div.status-box-open')).toBeVisible()
-    expect($('div.status-box-closed')).toBeHidden()
\ No newline at end of file
+    expect($('div.status-box-closed')).toBeHidden()