diff --git a/mode/css/css.js b/mode/css/css.js
index 7fcf335d30c83a14125858aab84e08848a0569e9..512bb29de616ac77187a6fac4658a8147f0b344c 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -28,7 +28,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       counterDescriptors = parserConfig.counterDescriptors || {},
       colorKeywords = parserConfig.colorKeywords || {},
       valueKeywords = parserConfig.valueKeywords || {},
-      allowNested = parserConfig.allowNested;
+      allowNested = parserConfig.allowNested,
+      supportsAtComponent = (typeof parserConfig.supportsAtComponent != "undefined" ?
+          parserConfig.supportsAtComponent : false);
 
   var type, override;
   function ret(style, tp) { type = tp; return style; }
@@ -127,6 +129,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     return type;
   }
 
+  function pushContextNoIndent(state, stream, type) {
+    state.context = new Context(type, stream.indentation(), state.context);
+    return type;
+  }
+
   function popContext(state) {
     state.context = state.context.prev;
     return state.context.type;
@@ -160,6 +167,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       return pushContext(state, stream, "block");
     } else if (type == "}" && state.context.prev) {
       return popContext(state);
+    } else if (supportsAtComponent && /@component/.test(type)) {
+      return pushContext(state, stream, "atComponentBlock");
     } else if (/@(media|supports|(-moz-)?document)/.test(type)) {
       return pushContext(state, stream, "atBlock");
     } else if (/@(font-face|counter-style)/.test(type)) {
@@ -286,6 +295,18 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     return state.context.type;
   };
 
+  states.atComponentBlock = function(type, stream, state) {
+    if (type == "}") return popAndPass(type, stream, state);
+    if (type == "{") {
+      return popContext(state) && pushContextNoIndent(state, stream, allowNested ? "block" : "top");
+    }
+
+    if (type == "word") {
+      override = "error";
+    }
+    return state.context.type;
+  };
+
   states.atBlock_parens = function(type, stream, state) {
     if (type == ")") return popContext(state);
     if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
@@ -769,4 +790,26 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     helperType: "less"
   });
 
+  CodeMirror.defineMIME("text/x-gss", {
+    documentTypes: documentTypes,
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    fontProperties: fontProperties,
+    counterDescriptors: counterDescriptors,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    supportsAtComponent: true,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
+      }
+    },
+    name: "css",
+    helperType: "gss"
+  });
+
 });
diff --git a/mode/css/gss.html b/mode/css/gss.html
new file mode 100644
index 0000000000000000000000000000000000000000..232fe8c12b4f250f1e672feb5b4f6ee48041c732
--- /dev/null
+++ b/mode/css/gss.html
@@ -0,0 +1,103 @@
+<!doctype html>
+
+<title>CodeMirror: Closure Stylesheets (GSS) mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<link rel="stylesheet" href="../../addon/hint/show-hint.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="css.js"></script>
+<script src="../../addon/hint/show-hint.js"></script>
+<script src="../../addon/hint/css-hint.js"></script>
+<style>.CodeMirror {background: #f8f8f8;}</style>
+<div id=nav>
+  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">Closure Stylesheets (GSS)</a>
+  </ul>
+</div>
+
+<article>
+<h2>Closure Stylesheets (GSS) mode</h2>
+<form><textarea id="code" name="code">
+/* Some example Closure Stylesheets */
+
+@provide 'some.styles';
+
+@require 'other.styles';
+
+@component {
+
+@def FONT_FAMILY           "Times New Roman", Georgia, Serif;
+@def FONT_SIZE_NORMAL      15px;
+@def FONT_NORMAL           normal FONT_SIZE_NORMAL FONT_FAMILY;
+
+@def BG_COLOR              rgb(235, 239, 249);
+
+@def DIALOG_BORDER_COLOR   rgb(107, 144, 218);
+@def DIALOG_BG_COLOR       BG_COLOR;
+
+@def LEFT_HAND_NAV_WIDTH    180px;
+@def LEFT_HAND_NAV_PADDING  3px;
+
+@defmixin size(WIDTH, HEIGHT) {
+  width: WIDTH;
+  height: HEIGHT;
+}
+
+body {
+  background-color: BG_COLOR;
+  margin: 0;
+  padding: 3em 6em;
+  font: FONT_NORMAL;
+  color: #000;
+}
+
+#navigation a {
+  font-weight: bold;
+  text-decoration: none !important;
+}
+
+.dialog {
+  background-color: DIALOG_BG_COLOR;
+  border: 1px solid DIALOG_BORDER_COLOR;
+}
+
+.content {
+  position: absolute;
+  margin-left: add(LEFT_HAND_NAV_PADDING,  /* padding left */
+                   LEFT_HAND_NAV_WIDTH,
+                   LEFT_HAND_NAV_PADDING); /* padding right */
+
+}
+
+.logo {
+  @mixin size(150px, 55px);
+  background-image: url('http://www.google.com/images/logo_sm.gif');
+}
+
+}
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        extraKeys: {"Ctrl-Space": "autocomplete"},
+        lineNumbers: true,
+        matchBrackets: "text/x-less",
+        mode: "text/x-gss"
+      });
+    </script>
+
+    <p>A mode for <a href="https://github.com/google/closure-stylesheets">Closure Stylesheets</a> (GSS).</p>
+    <p><strong>MIME type defined:</strong> <code>text/x-gss</code>.</p>
+
+    <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gss_*">normal</a>,  <a href="../../test/index.html#verbose,gss_*">verbose</a>.</p>
+
+  </article>
diff --git a/mode/css/gss_test.js b/mode/css/gss_test.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1a330652e5b657e5b4d1f188ca5dd7a645444cd
--- /dev/null
+++ b/mode/css/gss_test.js
@@ -0,0 +1,17 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+  "use strict";
+
+  var mode = CodeMirror.getMode({indentUnit: 2}, "gss");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); }
+
+  MT("atComponent",
+     "[def @component] {",
+     "[tag foo] {",
+     "  [property color]: [keyword black];",
+     "}",
+     "}");
+
+})();
diff --git a/mode/css/test.js b/mode/css/test.js
index cda07748b25ee758dde5eab73c3b9314f2b52826..7d78135de494acd814e4c675f01dad3e80b8f6bd 100644
--- a/mode/css/test.js
+++ b/mode/css/test.js
@@ -6,7 +6,7 @@
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
 
   // Error, because "foobarhello" is neither a known type or property, but
-  // property was expected (after "and"), and it should be in parenthese.
+  // property was expected (after "and"), and it should be in parentheses.
   MT("atMediaUnknownType",
      "[def @media] [attribute screen] [keyword and] [error foobarhello] { }");
 
diff --git a/mode/index.html b/mode/index.html
index ca1703aff9cff927ec898d6162e00d59056b75eb..9bb8beff92d3a24549b42d362d89307172d91ce1 100644
--- a/mode/index.html
+++ b/mode/index.html
@@ -36,6 +36,7 @@ option.</p>
       <li><a href="brainfuck/index.html">Brainfuck</a></li>
       <li><a href="clike/index.html">C, C++, C#</a></li>
       <li><a href="clojure/index.html">Clojure</a></li>
+      <li><a href="css/gss.html">Closure Stylesheets (GSS)</a></li>
       <li><a href="cmake/index.html">CMake</a></li>
       <li><a href="cobol/index.html">COBOL</a></li>
       <li><a href="coffeescript/index.html">CoffeeScript</a></li>
diff --git a/mode/meta.js b/mode/meta.js
index 376e1ecdc25e09a95e89a8c4ec83b419f2162fcb..aff0cc7c62167a84b2326e3de5325a3fd82271c7 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -22,6 +22,7 @@
     {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
     {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
     {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
+    {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
     {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
     {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
     {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
diff --git a/test/index.html b/test/index.html
index 310e88519d167b03c4045c4dae30d189226883c6..e0593e90f36fa5cd6aa791a0081907cfb7398980 100644
--- a/test/index.html
+++ b/test/index.html
@@ -20,6 +20,7 @@
 <script src="../mode/clike/clike.js"></script>
 <!-- clike must be after css or vim and sublime tests will fail -->
 <script src="../mode/gfm/gfm.js"></script>
+<script src="../mode/gss/gss.js"></script>
 <script src="../mode/haml/haml.js"></script>
 <script src="../mode/htmlmixed/htmlmixed.js"></script>
 <script src="../mode/javascript/javascript.js"></script>
@@ -100,6 +101,7 @@
 
     <script src="../mode/clike/test.js"></script>
     <script src="../mode/css/test.js"></script>
+    <script src="../mode/css/gss_test.js"></script>
     <script src="../mode/css/scss_test.js"></script>
     <script src="../mode/css/less_test.js"></script>
     <script src="../mode/gfm/test.js"></script>