diff --git a/assets/images/icons.png b/assets/images/icons.png
index 1a5f97e4..7320379f 100644
Binary files a/assets/images/icons.png and b/assets/images/icons.png differ
diff --git a/assets/images/icons@2x.png b/assets/images/icons@2x.png
index 534dc4df..dda527a9 100644
Binary files a/assets/images/icons@2x.png and b/assets/images/icons@2x.png differ
diff --git a/assets/javascripts/templates/pages/about_tmpl.coffee b/assets/javascripts/templates/pages/about_tmpl.coffee
index 7d009e9b..a004fda3 100644
--- a/assets/javascripts/templates/pages/about_tmpl.coffee
+++ b/assets/javascripts/templates/pages/about_tmpl.coffee
@@ -125,6 +125,11 @@ credits = [
'Google, Inc.',
'CC BY',
'http://creativecommons.org/licenses/by/3.0/'
+ ], [
+ 'Grunt',
+ '2014 Grunt Team',
+ 'MIT',
+ 'https://raw.githubusercontent.com/gruntjs/gruntjs.com/master/LICENSE'
], [
'HTTP',
'1999 The Internet Society',
diff --git a/assets/javascripts/templates/pages/news_tmpl.coffee b/assets/javascripts/templates/pages/news_tmpl.coffee
index 61fd5e48..d91fcccb 100644
--- a/assets/javascripts/templates/pages/news_tmpl.coffee
+++ b/assets/javascripts/templates/pages/news_tmpl.coffee
@@ -25,7 +25,7 @@ newsItem = (date, news) ->
app.news = [
[ 1399075200000, # May 3, 2014
- """ New Express documentation """,
+ """ New Express and Grunt documentations """,
], [
1396742400000, # April 6, 2014
""" New Go documentation """,
diff --git a/assets/javascripts/views/pages/grunt.coffee b/assets/javascripts/views/pages/grunt.coffee
new file mode 100644
index 00000000..2b8695b3
--- /dev/null
+++ b/assets/javascripts/views/pages/grunt.coffee
@@ -0,0 +1,4 @@
+#= require views/pages/base
+#= require views/pages/underscore
+
+app.views.GruntPage = app.views.UnderscorePage
diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss
index 36cea34c..011e97cb 100644
--- a/assets/stylesheets/application.css.scss
+++ b/assets/stylesheets/application.css.scss
@@ -38,6 +38,7 @@
'pages/jquery',
'pages/knockout',
'pages/git',
+ 'pages/grunt',
'pages/less',
'pages/lodash',
'pages/mdn',
diff --git a/assets/stylesheets/global/_icons.scss b/assets/stylesheets/global/_icons.scss
index f6eeb97c..0f0ab8d4 100644
--- a/assets/stylesheets/global/_icons.scss
+++ b/assets/stylesheets/global/_icons.scss
@@ -54,3 +54,4 @@
._icon-cpp:before { background-position: 0 -8rem; }
._icon-go:before { background-position: -1rem -8rem; }
._icon-express:before { background-position: -2rem -8rem; }
+._icon-grunt:before { background-position: -3rem -8rem; }
diff --git a/assets/stylesheets/pages/_grunt.scss b/assets/stylesheets/pages/_grunt.scss
new file mode 100644
index 00000000..592653fb
--- /dev/null
+++ b/assets/stylesheets/pages/_grunt.scss
@@ -0,0 +1,8 @@
+._grunt {
+ padding-left: 1rem;
+
+ h1, h2, h3 { margin-left: -1rem; }
+ h2 { @extend %block-heading; }
+ h3 { @extend %block-label, %label-blue; }
+ p > code { @extend %label; }
+}
diff --git a/lib/docs/filters/grunt/clean_html.rb b/lib/docs/filters/grunt/clean_html.rb
new file mode 100644
index 00000000..f1ddf5e9
--- /dev/null
+++ b/lib/docs/filters/grunt/clean_html.rb
@@ -0,0 +1,28 @@
+module Docs
+ class Grunt
+ class CleanHtmlFilter < Filter
+ def call
+ @doc = at_css('.hero-unit')
+
+ if root_page?
+ at_css('h1').content = 'Grunt'
+ end
+
+ css('.end-link').remove
+
+ # Put id attributes on headings
+ css('a.anchor').each do |node|
+ node.parent['id'] = node['name']
+ node.before(node.children).remove
+ end
+
+ # Remove code highlighting
+ css('pre').each do |node|
+ node.content = node.content
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/grunt/entries.rb b/lib/docs/filters/grunt/entries.rb
new file mode 100644
index 00000000..01b453e1
--- /dev/null
+++ b/lib/docs/filters/grunt/entries.rb
@@ -0,0 +1,34 @@
+module Docs
+ class Grunt
+ class EntriesFilter < Docs::EntriesFilter
+ def get_name
+ at_css('h1').content
+ end
+
+ def get_type
+ if name.starts_with?('grunt') || name == 'Inside Tasks'
+ name
+ else
+ 'Miscellaneous'
+ end
+ end
+
+ def additional_entries
+ return [] unless subpath.starts_with?('api')
+
+ css('h3').each_with_object [] do |node, entries|
+ name = node.content
+ name.remove! %r{\s.+\z}
+
+ next if name == self.name
+
+ entries << [name, node['id']]
+ end
+ end
+
+ def include_default_entry?
+ name != 'Inside Tasks'
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/grunt.rb b/lib/docs/scrapers/grunt.rb
new file mode 100644
index 00000000..8d00a434
--- /dev/null
+++ b/lib/docs/scrapers/grunt.rb
@@ -0,0 +1,27 @@
+module Docs
+ class Grunt < UrlScraper
+ self.name = 'Grunt'
+ self.type = 'grunt'
+ self.version = '0.4.4'
+ self.base_url = 'http://gruntjs.com/'
+ self.root_path = 'getting-started'
+ self.initial_paths = %w(api/grunt)
+
+ html_filters.push 'grunt/clean_html', 'grunt/entries'
+
+ options[:only] = %w(
+ configuring-tasks
+ sample-gruntfile
+ creating-tasks
+ using-the-cli
+ )
+ options[:only_patterns] = [/\Aapi\//]
+
+ options[:container] = '.container > .row-fluid'
+
+ options[:attribution] = <<-HTML
+ © 2014 Grunt Team
+ Licensed under the MIT License.
+ HTML
+ end
+end
diff --git a/public/icons/docs/grunt/16.png b/public/icons/docs/grunt/16.png
new file mode 100644
index 00000000..c8df7ddf
Binary files /dev/null and b/public/icons/docs/grunt/16.png differ
diff --git a/public/icons/docs/grunt/16@2x.png b/public/icons/docs/grunt/16@2x.png
new file mode 100644
index 00000000..ffe45436
Binary files /dev/null and b/public/icons/docs/grunt/16@2x.png differ
diff --git a/public/icons/docs/grunt/SOURCE b/public/icons/docs/grunt/SOURCE
new file mode 100644
index 00000000..b8d4f1f1
--- /dev/null
+++ b/public/icons/docs/grunt/SOURCE
@@ -0,0 +1 @@
+https://github.com/gruntjs/gruntjs.com/tree/master/src/img