diff --git a/Makefile b/Makefile
index 87be9b73b1358d6a686938474d99b903e9dcf7d7..61a301c630326cd89e1f8fdeab1e3b6faa6571f4 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,7 @@ motivation.html : motivation.md _layouts/slides.html
 	-o $@ $<
 
 ## unittest : Run unit test (for Python 2 and 3)
-unittest: tools/check tools/validation_helpers.py tools/test_check.py
+unittest: tools/check.py tools/validation_helpers.py tools/test_check.py
 	cd tools/ && python2 test_check.py
 	cd tools/ && python3 test_check.py
 
diff --git a/css/swc.css b/css/swc.css
index b29dc3f6d57c62c09586a80f522345467c2f84d2..73bb7268126974569e317d8a989fd5873d81607c 100644
--- a/css/swc.css
+++ b/css/swc.css
@@ -18,11 +18,6 @@ div.chapter h2 {
     font-style: italic;
 }
 
-/* Objectives and key points */
-.objectives, .keypoints {
-    background-color: azure;
-}
-
 /* Things to fix. */
 .fixme {
     text-decoration: underline;
@@ -124,6 +119,14 @@ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
     padding: 0em 1em;
 }
 
+/* Challenges */
+.challenge {
+    background-color: #CCFFCC;
+    border: 5px solid #CCFFCC;
+    margin: 1em 0;
+    padding: 0em 1em;
+}
+
 /* Things to fix. */
 .fixme {
     text-decoration: underline;
diff --git a/requirements.txt b/requirements.txt
index 8551c266dad9c585188ffaeff5ac0d0d9e4b6b4c..695ebe4e86d8ae222a62b8cf06ac937f61720a9f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
 CommonMark
-pandocfilters
\ No newline at end of file
+pandocfilters
+PyYAML
diff --git a/tools/check b/tools/check.py
similarity index 93%
rename from tools/check
rename to tools/check.py
index ecfaaba9724a5971dc5f08007fb0bca1b6f3a644..873ed2522290dd9f7ec94dc646026e0ec5474ab9 100755
--- a/tools/check
+++ b/tools/check.py
@@ -20,21 +20,8 @@ import os
 import re
 import sys
 
-try:
-    # Code tested with CommonMark version 0.5.4; API may change
-    import CommonMark
-except ImportError:
-    ERROR_MESSAGE = """This program requires the CommonMark python package.
-Install using
-
-    # pip install commonmark
-
-or
-
-    # easy_install commonmark
-"""
-    print(ERROR_MESSAGE)
-    sys.exit(1)
+import CommonMark
+import yaml
 
 import validation_helpers as vh
 
@@ -101,9 +88,8 @@ class MarkdownValidator(object):
                 valid = False
         return valid
 
-    def _validate_one_doc_header_row(self, text):
+    def _validate_one_doc_header_row(self, label, content):
         """Validate a single row of the document header section"""
-        label, content = text.split(":", 1)
         if label not in self.DOC_HEADERS:
             logging.warning(
                 "In {0}: "
@@ -116,8 +102,8 @@ class MarkdownValidator(object):
         if not validate_header:
             logging.error(
                 "In {0}: "
-                "Document header field for label {1} "
-                "does not follow expected format".format(self.filename, label))
+                "Contents of document header field for label {1} "
+                "do not follow expected format".format(self.filename, label))
         return validate_header
 
     # Methods related to specific validation. Can override specific tests.
@@ -127,21 +113,37 @@ class MarkdownValidator(object):
         Pass only if the header of the document contains the specified
             sections with the expected contents"""
 
-        # Header section should be wrapped in hrs
+        # Test: Header section should be wrapped in hrs
         has_hrs = self._validate_hrs()
 
-        # Labeled sections in the actual headers should match expected format
         header_node = self.ast.children[1]
-        test_headers = [self._validate_one_doc_header_row(s)
-                        for s in header_node.strings]
+        header_text = '\n'.join(header_node.strings)
+
+        # Parse headers as YAML. Don't check if parser returns None or str.
+        header_yaml = yaml.load(header_text)
+        if not isinstance(header_yaml, dict):
+            logging.error("In {0}: "
+                          "Expected YAML markup with labels "
+                          "{1}".format(self.filename, self.DOC_HEADERS.keys()))
+            return False
+
+        # Test: Labeled YAML should match expected format
+        test_headers = [self._validate_one_doc_header_row(k, v)
+                        for k, v in header_yaml.items()]
 
-        # Must have all expected header lines, and no others.
-        only_headers = (len(header_node.strings) == len(self.DOC_HEADERS))
+        # Test: Must have all expected header lines, and no others.
+        only_headers = (len(header_yaml) == len(self.DOC_HEADERS))
 
-        # Headings must appear in the order expected
-        valid_order = self._validate_section_heading_order()
+        # If expected headings are missing, print an informative message
+        missing_headings = [h for h in self.DOC_HEADERS
+                            if h not in header_yaml]
 
-        return has_hrs and all(test_headers) and only_headers and valid_order
+        for h in missing_headings:
+            logging.error("In {0}: "
+                          "Header section is missing expected "
+                          "row {1}".format(self.filename, h))
+
+        return has_hrs and all(test_headers) and only_headers
 
     def _validate_section_heading_order(self, ast_node=None, headings=None):
         """Verify that section headings appear, and in the order expected"""
@@ -344,8 +346,8 @@ class IndexPageValidator(MarkdownValidator):
         return super(IndexPageValidator, self)._validate_links(links_to_skip)
 
     def _run_tests(self):
-        tests = [self._validate_intro_section()]
         parent_tests = super(IndexPageValidator, self)._run_tests()
+        tests = [self._validate_intro_section()]
         return all(tests) and parent_tests
 
 
@@ -397,9 +399,9 @@ class TopicPageValidator(MarkdownValidator):
         return False
 
     def _run_tests(self):
+        parent_tests = super(TopicPageValidator, self)._run_tests()
         tests = [self._validate_has_no_headings(),
                  self._validate_learning_objective()]
-        parent_tests = super(TopicPageValidator, self)._run_tests()
         return all(tests) and parent_tests
 
 
@@ -496,7 +498,7 @@ class LicensePageValidator(MarkdownValidator):
     def _run_tests(self):
         """Skip the base tests; just check md5 hash"""
         # TODO: This hash is specific to the license for english-language repo
-        expected_hash = '258aa6822fa77f7c49c37c3759017891'
+        expected_hash = 'cd5742b6596a1f2f35c602ad43fa24b2'
         m = hashlib.md5()
         try:
             m.update(self.markdown)
@@ -658,7 +660,3 @@ def main(parsed_args_obj):
 if __name__ == "__main__":
     parsed_args = command_line()
     main(parsed_args)
-
-    #### Sample of how validator is used directly
-    # validator = HomePageValidator('../index.md')
-    # print validator.validate()
diff --git a/tools/test_check.py b/tools/test_check.py
index 003e26e82ef8086335a4c1e14565ad1c0a58acd7..0451c8444d914aca6ab9fe2635575f49814dfd34 100644
--- a/tools/test_check.py
+++ b/tools/test_check.py
@@ -7,14 +7,11 @@ Some of these tests require looking for example files, which exist only on
 the gh-pages branch.   Some tests may therefore fail on branch "core".
 """
 
-
-import imp
 import logging
 import os
 import unittest
 
-check = imp.load_source("check",  # Import non-.py file
-                        os.path.join(os.path.dirname(__file__), "check"))
+import check
 
 # Make log messages visible to help audit test failures
 check.start_logging(level=logging.DEBUG)
@@ -63,7 +60,6 @@ class TestIndexPage(BaseTemplateTest):
 
 layout: lesson
 title: Lesson Title
-keywords: ["some", "key terms", "in a list"]
 
 Another section that isn't an HR
 """)
@@ -74,7 +70,6 @@ Another section that isn't an HR
         """One of the required headers is missing"""
         validator = self._create_validator("""---
 layout: lesson
-keywords: ["some", "key terms", "in a list"]
 ---""")
         self.assertFalse(validator._validate_doc_headers())
 
@@ -83,16 +78,14 @@ keywords: ["some", "key terms", "in a list"]
         validator = self._create_validator("""---
 layout: lesson
 title: Lesson Title
-keywords: ["some", "key terms", "in a list"]
 otherline: Nothing
 ---""")
         self.assertFalse(validator._validate_doc_headers())
 
-    def test_headers_fail_because_invalid_content(self):
+    def test_fail_when_headers_not_yaml_dict(self):
+        """Fail when the headers can't be parsed to a dict of YAML data"""
         validator = self._create_validator("""---
-layout: lesson
-title: Lesson Title
-keywords: this is not a list
+This will parse as a string, not a dictionary
 ---""")
         self.assertFalse(validator._validate_doc_headers())
 
@@ -158,7 +151,6 @@ Paragraph of introductory material.
         validator = self._create_validator("""---
 layout: lesson
 title: Lesson Title
-keywords: ["some", "key terms", "in a list"]
 ---
 Paragraph of introductory material.
 
@@ -185,7 +177,6 @@ Paragraph of introductory material.
         validator = self._create_validator("""---
 layout: lesson
 title: Lesson Title
-keywords: ["some", "key terms", "in a list"]
 ---
 Paragraph of introductory material.
 
@@ -200,7 +191,6 @@ Paragraph of introductory material.
         validator = self._create_validator("""---
 layout: lesson
 title: Lesson Title
-keywords: ["some", "key terms", "in a list"]
 ---
 Paragraph of introductory material.
 
@@ -295,6 +285,16 @@ class TestTopicPage(BaseTemplateTest):
     SAMPLE_FILE = os.path.join(MARKDOWN_DIR, "01-one.md")
     VALIDATOR = check.TopicPageValidator
 
+    def test_headers_fail_because_invalid_content(self):
+        """The value provided as YAML does not match the expected datatype"""
+        validator = self._create_validator("""---
+layout: lesson
+title: Lesson Title
+subtitle: A page
+minutes: not a number
+---""")
+        self.assertFalse(validator._validate_doc_headers())
+
     def test_sample_file_passes_validation(self):
         sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
         res = sample_validator.validate()