Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1051
diff -u -p -r1.1051 common.inc
--- includes/common.inc	21 Nov 2009 00:43:42 -0000	1.1051
+++ includes/common.inc	21 Nov 2009 22:26:16 -0000
@@ -5557,6 +5557,9 @@ function drupal_common_theme() {
     'form_required_marker' => array(
       'arguments' => array('element' => NULL),
     ),
+    'form_error_marker' => array(
+      'render element' => 'element',
+    ),
     'text_format_wrapper' => array(
       'render element' => 'element',
     ),
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.402
diff -u -p -r1.402 form.inc
--- includes/form.inc	21 Nov 2009 14:06:45 -0000	1.402
+++ includes/form.inc	21 Nov 2009 22:26:16 -0000
@@ -853,7 +853,10 @@ function _form_validate($elements, &$for
       // An unchecked checkbox has a #value of numeric 0, different than string
       // '0', which could be a valid value.
       if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === 0)) {
-        form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
+        form_error($elements, $t('<a href="#!field_id">!name</a> field is required.', array(
+          '!field_id' => $elements['#id'],
+          '!name' => $elements['#title'],
+        )));
       }
 
       // Verify that the value is not longer than #maxlength.
@@ -2812,14 +2815,15 @@ function theme_form_element($variables) 
 
   $output = '<div class="' . implode(' ', $class) . '">' . "\n";
   $required = !empty($element['#required']) ? theme('form_required_marker', array('element' => $element)) : '';
+  $error = theme('form_error_marker', array('element' => $element));
 
   if (!empty($element['#title']) && empty($element['#form_element_skip_title'])) {
     $title = $element['#title'];
     if (!empty($element['#id'])) {
-      $output .= ' <label for="' . $element['#id'] . '">' . $t('!title !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
+      $output .= ' <label for="' . $element['#id'] . '">' . $t('!title !required !error', array('!title' => filter_xss_admin($title), '!required' => $required, '!error' => $error)) . "</label>\n";
     }
     else {
-      $output .= ' <label>' . $t('!title !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
+      $output .= ' <label>' . $t('!title !required !error', array('!title' => filter_xss_admin($title), '!required' => $required, '!error' => $error)) . "</label>\n";
     }
   }
 
@@ -2856,6 +2860,44 @@ function theme_form_required_marker($var
 }
 
 /**
+ * Theme the error marker for form elements.
+ *
+ * By default this function outputs the error message, if any, in an invisible
+ * span. This allows screen reader users to identify the fields with errors.
+ * Override this function to provide an alternative error marker, such as
+ * visible error text or an indicator with a tooltip to show the full error.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ * @return
+ *   A string with a marker to identify an error, otherwise an empty string.
+ *
+ * @ingroup themeable
+ */
+function theme_form_error_marker($variables) {
+  $element = $variables['element'];
+  // This is also used in the installer, pre-database setup.
+  $t = get_t();
+
+  $output = '';
+  if ($raw_error = form_get_error($element)) {
+    // A simple call to empty() will not cut it here as some fields, like
+    // checkboxes, can return a valid value of '0'. Instead, check the
+    // length if it's a string, and the item count if it's an array.
+    $error = ($element['#required'] && (!count($element['#value']) ||
+      (is_string($element['#value']) && strlen(trim($element['#value'])) == 0))) ?
+      $t('This field is required.') : strip_tags($raw_error);
+    $attributes = array(
+      'class' => array('element-invisible', 'error', 'error-marker'),
+    );
+    $output .= ' <span' . drupal_attributes($attributes) . '>' . $error . '</span>';
+  }
+
+  return $output;
+}
+
+/**
  * Sets a form element's class attribute.
  *
  * Adds 'required' and 'error' classes as needed.
Index: modules/field/tests/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v
retrieving revision 1.3
diff -u -p -r1.3 field.test
--- modules/field/tests/field.test	21 Nov 2009 14:35:05 -0000	1.3
+++ modules/field/tests/field.test	21 Nov 2009 22:26:17 -0000
@@ -1432,7 +1432,7 @@ class FieldFormTestCase extends FieldTes
     // Submit with missing required value.
     $edit = array();
     $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertText(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
 
     // Create an entity
     $value = mt_rand(1, 127);
@@ -1448,7 +1448,7 @@ class FieldFormTestCase extends FieldTes
     $value = '';
     $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
     $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertText(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
   }
 
 //  function testFieldFormMultiple() {
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.25
diff -u -p -r1.25 form.test
--- modules/simpletest/tests/form.test	18 Nov 2009 18:51:11 -0000	1.25
+++ modules/simpletest/tests/form.test	21 Nov 2009 22:26:17 -0000
@@ -63,7 +63,7 @@ class FormsTestCase extends DrupalWebTes
     $elements['file']['empty_values'] = $empty_strings;
 
     // Regular expression to find the expected marker on required elements.
-    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span></label>@';
+    $required_marker_preg = '@<label.*<span class="form-required" title="This field is required\.">\*</span>.*</label>@';
 
     // Go through all the elements and all the empty values for them.
     foreach ($elements as $type => $data) {
@@ -128,7 +128,7 @@ class FormsTestCase extends DrupalWebTes
     // First, try to submit without the required checkbox.
     $edit = array();
     $this->drupalPost('form-test/checkbox', $edit, t('Submit'));
-    $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
+    $this->assertText(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
 
     // Now try to submit the form correctly.
     $this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit'));
Index: themes/seven/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/seven/style.css,v
retrieving revision 1.26
diff -u -p -r1.26 style.css
--- themes/seven/style.css	21 Nov 2009 18:18:21 -0000	1.26
+++ themes/seven/style.css	21 Nov 2009 22:26:17 -0000
@@ -161,6 +161,11 @@ div.error {
   border-color: #d52;
 }
 
+div.error a {
+  color: #fff;
+  text-decoration: underline;
+}
+
 div.status {
   color: #360;
   background: #cf8;
