diff --git a/src/Element/ComponentForm.php b/src/Element/ComponentForm.php
index 220bbc0dc964067c376f9c1a86d8756348e93d02..9db7bc23b2ff666dbd11e0ead371f1bda8fa5f93 100644
--- a/src/Element/ComponentForm.php
+++ b/src/Element/ComponentForm.php
@@ -263,11 +263,51 @@ class ComponentForm extends ComponentFormBase {
     return $sub_form;
   }
 
+  /**
+   * Open wrapped elements with errors.
+   *
+   * @param array $element
+   *   The element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   *
+   * @return bool
+   *   return TRUE if errors were found.
+   */
+  protected static function openWrappedElementsWithErrors(array &$element, FormStateInterface $form_state) : bool {
+    $errors = $form_state->getErrors();
+    if (count($errors) === 0) {
+      return FALSE;
+    }
+    $element_name = implode("][", $element["#parents"]);
+    $error_elements_found = FALSE;
+    foreach (array_keys($errors) as $error_name) {
+      if (!str_starts_with($error_name, $element_name)) {
+        continue;
+      }
+      $error_elements_found = TRUE;
+      $parents = array_slice(explode("][", $error_name), count($element["#parents"]));
+      if (count($parents) < 2) {
+        continue;
+      }
+      $parents_of_prop_or_slot = array_slice($parents, 0, 2);
+      $prop_or_slot = NestedArray::getValue($element, $parents_of_prop_or_slot);
+      if (!empty($prop_or_slot) && isset($prop_or_slot["#wrap"]) && $prop_or_slot["#wrap"]) {
+        $parents_of_prop_or_slot[] = $parents_of_prop_or_slot[1];
+        $parents_of_prop_or_slot[] = "#open";
+        NestedArray::setValue($element, $parents_of_prop_or_slot, TRUE);
+      }
+    }
+    return $error_elements_found;
+  }
+
   /**
    * Form element validation handler.
    */
   public static function validateFormElement(array &$element, FormStateInterface $form_state) : void {
-
+    if (static::openWrappedElementsWithErrors($element, $form_state)) {
+      return;
+    }
     try {
       $trigger_element = $form_state->getTriggeringElement();
       if (isset($trigger_element['#ui_patterns']) === FALSE) {
diff --git a/src/Plugin/UiPatterns/Source/AttributesWidget.php b/src/Plugin/UiPatterns/Source/AttributesWidget.php
index 6c0581292be69d16b2663e74686615b1c170bbf5..cb2d64af7a3edd6abaf03c11b2e71f49b776aab8 100644
--- a/src/Plugin/UiPatterns/Source/AttributesWidget.php
+++ b/src/Plugin/UiPatterns/Source/AttributesWidget.php
@@ -64,13 +64,15 @@ class AttributesWidget extends SourcePluginBase {
   protected function buildRegexPattern(): string {
     // Attribute names are a mix of ASCII lower and upper alphas.
     $attr_name = "[a-zA-Z\-]+";
-    // Allow anything in attributes values, which are between double quotes.
-    $double_quoted_value = '"[\s\w\-]*"';
+    // Discard double quotes which are used for delimiting.
+    $double_quoted_value = '[^"]*';
     $space = "\s*";
-    $attr = $attr_name . "=" . $double_quoted_value . $space;
-    // The pattern must match the entire input's value, rather than matching a
-    // substring - as if a ^(?: were implied at the start of the pattern and )$
-    // at the end.
+    $attr = sprintf("%s=\"%s\"%s", $attr_name, $double_quoted_value, $space);
+    // Start and end delimiters are not expected here, they will be added:
+    // - by \Drupal\Core\Render\Element\FormElementBase::validatePattern for
+    //   server side validation
+    // - in the HTML5 pattern attribute, for client side validation
+    // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern
     return $space . "(" . $attr . ")*";
   }