diff --git a/core/modules/inline_form_errors/inline_form_errors.module b/core/modules/inline_form_errors/inline_form_errors.module
index d5c40b4..b8f316f 100644
--- a/core/modules/inline_form_errors/inline_form_errors.module
+++ b/core/modules/inline_form_errors/inline_form_errors.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\inline_form_errors\SupportedRenderElementHelper;
 
 /**
  * Implements hook_help().
@@ -55,6 +56,14 @@ function inline_form_errors_preprocess_datetime_wrapper(&$variables) {
 }
 
 /**
+ * Implements hook_element_info_alter().
+ */
+function inline_form_errors_element_info_alter(array &$info) {
+  \Drupal::classResolver()->getInstanceFromDefinition(SupportedRenderElementHelper::class)
+    ->alterElementInfo($info);
+}
+
+/**
  * Populates form errors in the template.
  */
 function _inline_form_errors_set_errors(&$variables) {
diff --git a/core/modules/inline_form_errors/src/FormErrorHandler.php b/core/modules/inline_form_errors/src/FormErrorHandler.php
index 2b892e9..2bcbfc6 100644
--- a/core/modules/inline_form_errors/src/FormErrorHandler.php
+++ b/core/modules/inline_form_errors/src/FormErrorHandler.php
@@ -47,15 +47,23 @@ public function __construct(TranslationInterface $string_translation, LinkGenera
   /**
    * Loops through and displays all form errors.
    *
+   * To disable inline form errors for an entire form set the
+   * #disable_inline_form_errors property to TRUE on the top level of the $form
+   * array:
+   * @code
+   * $form['#disable_inline_form_errors'] = TRUE;
+   * @endcode
+   * This should only be done when another appropriate accessibility strategy is
+   * in place.
+   *
    * @param array $form
    *   An associative array containing the structure of the form.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The current state of the form.
    */
   protected function displayErrorMessages(array $form, FormStateInterface $form_state) {
-    // Use the original error display for Quick Edit forms, because in this case
-    // the errors are already near the form element.
-    if ($form['#form_id'] === 'quickedit_field_form') {
+    // Skip generating inline form errors when opted out.
+    if (!empty($form['#disable_inline_form_errors'])) {
       parent::displayErrorMessages($form, $form_state);
       return;
     }
diff --git a/core/modules/inline_form_errors/src/SupportedRenderElementHelper.php b/core/modules/inline_form_errors/src/SupportedRenderElementHelper.php
new file mode 100644
index 0000000..b3a82ba
--- /dev/null
+++ b/core/modules/inline_form_errors/src/SupportedRenderElementHelper.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Drupal\inline_form_errors;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\FormElementInterface;
+use Drupal\Core\Render\ElementInfoManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides functionality to determine and process supported render elements.
+ */
+class SupportedRenderElementHelper implements ContainerInjectionInterface {
+
+  /**
+   * A list of pseudo form elements.
+   *
+   * Define a list of elements that are not form elements but are primarily
+   * used in forms and supported by Inline Form Errors.
+   *
+   * @var array
+   */
+  protected $pseudoFormElements = [
+    'details',
+    'fieldset',
+    'datetime_wrapper',
+  ];
+
+  /**
+   * The element info manager.
+   *
+   * @var \Drupal\Core\Render\ElementInfoManagerInterface
+   */
+  protected $elementInfoManager;
+
+  /**
+   * Constructs a new SupportedRenderElementHelper.
+   *
+   * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info_manager
+   *   The element info manager.
+   */
+  public function __construct(ElementInfoManagerInterface $element_info_manager) {
+    $this->elementInfoManager = $element_info_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('plugin.manager.element_info'));
+  }
+
+  /**
+   * Determines if an element type supports Inline Form Errors.
+   *
+   * @param string $element_type
+   *   The element type.
+   *
+   * @return bool
+   *   TRUE if the given element type is a form element or a render element
+   *   which supports Inline Form Errors, or FALSE otherwise.
+   */
+  public function supportsInlineFormErrors($element_type) {
+    return in_array($element_type, $this->pseudoFormElements) || $this->isFormElement($element_type);
+  }
+
+  /**
+   * Determines if an element type is a form element.
+   *
+   * @param string $element_type
+   *   The element type.
+   *
+   * @return bool
+   *   TRUE if the given element type implements FormElementInterface or FALSE
+   *   otherwise.
+   */
+  protected function isFormElement($element_type) {
+    if ($this->elementInfoManager->hasDefinition($element_type)) {
+      $definition = $this->elementInfoManager->getDefinition($element_type);
+      return is_subclass_of($definition['class'], FormElementInterface::class);
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Alters the element type info.
+   *
+   * @param array $info
+   *   An associative array with structure identical to that of the return value
+   *   of \Drupal\Core\Render\ElementInfoManagerInterface::getInfo().
+   */
+  public function alterElementInfo(array &$info) {
+    foreach ($info as $element_type => $element_info) {
+      if ($this->supportsInlineFormErrors($element_type)) {
+        $info[$element_type]['#process'][] = [static::class, 'processFormElement'];
+      }
+    }
+  }
+
+  /**
+   * Process all form elements and pseudo form elements.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   element. Note that $element must be taken by reference here, so processed
+   *   child elements are taken over into $form_state.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processFormElement(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    // Prevent displaying inline form errors when disabled for the whole form.
+    if (!empty($complete_form['#disable_inline_form_errors'])) {
+      $element['#error_no_message'] = TRUE;
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/modules/inline_form_errors/tests/src/Kernel/FormElementInlineErrorTest.php b/core/modules/inline_form_errors/tests/src/Kernel/FormElementInlineErrorTest.php
new file mode 100644
index 0000000..4bf389e
--- /dev/null
+++ b/core/modules/inline_form_errors/tests/src/Kernel/FormElementInlineErrorTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\Tests\inline_form_errors\Kernel;
+
+use Drupal\Core\Form\FormState;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests messages on form elements.
+ *
+ * @group InlineFormErrors
+ */
+class FormElementInlineErrorTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['inline_form_errors'];
+
+  /**
+   * Tests that no inline form errors are shown when disabled for a form.
+   */
+  public function testDisplayErrorMessagesNotInline() {
+    $form_id = 'test';
+
+    $form = [
+      '#parents' => [],
+      '#disable_inline_form_errors' => TRUE,
+      '#array_parents' => [],
+    ];
+    $form['test'] = [
+      '#type' => 'textfield',
+      '#title' => 'Test',
+      '#parents' => ['test'],
+      '#id' => 'edit-test',
+      '#array_parents' => ['test'],
+    ];
+    $form_state = new FormState();
+
+    \Drupal::formBuilder()->prepareForm($form_id, $form, $form_state);
+    \Drupal::formBuilder()->processForm($form_id, $form, $form_state);
+
+    // Just test if the #error_no_message property is TRUE. FormErrorHandlerTest
+    // tests if the property actually hides the error message.
+    $this->assertArraySubset(['#error_no_message' => TRUE], $form['test']);
+  }
+
+}
diff --git a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
index acd255b..f110b36 100644
--- a/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
+++ b/core/modules/inline_form_errors/tests/src/Unit/FormErrorHandlerTest.php
@@ -140,12 +140,9 @@ public function testSetElementErrorsFromFormState() {
   }
 
   /**
-   * Test that Quick Edit forms show non-inline errors.
-   *
-   * @covers ::handleFormErrors
-   * @covers ::displayErrorMessages
+   * Tests that opting out of Inline Form Errors works.
    */
-  public function testDisplayErrorMessagesNotInlineQuickEdit() {
+  public function testDisplayErrorMessagesNotInline() {
     $form_error_handler = $this->getMockBuilder(FormErrorHandler::class)
       ->setConstructorArgs([$this->getStringTranslationStub(), $this->getMock(LinkGeneratorInterface::class), $this->getMock(RendererInterface::class)])
       ->setMethods(['drupalSetMessage'])
@@ -157,7 +154,7 @@ public function testDisplayErrorMessagesNotInlineQuickEdit() {
 
     $form = [
       '#parents' => [],
-      '#form_id' => 'quickedit_field_form',
+      '#disable_inline_form_errors' => TRUE,
       '#array_parents' => [],
     ];
     $form['test'] = [
@@ -165,7 +162,7 @@ public function testDisplayErrorMessagesNotInlineQuickEdit() {
       '#title' => 'Test',
       '#parents' => ['test'],
       '#id' => 'edit-test',
-      '#array_parents' => ['test']
+      '#array_parents' => ['test'],
     ];
     $form_state = new FormState();
     $form_state->setErrorByName('test', 'invalid');
diff --git a/core/modules/inline_form_errors/tests/src/Unit/SupportedRenderElementHelperTest.php b/core/modules/inline_form_errors/tests/src/Unit/SupportedRenderElementHelperTest.php
new file mode 100644
index 0000000..ebce4bd
--- /dev/null
+++ b/core/modules/inline_form_errors/tests/src/Unit/SupportedRenderElementHelperTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\Tests\inline_form_errors\Unit;
+
+use Drupal\Core\Render\Element\FormElement;
+use Drupal\Core\Render\Element\RenderElement;
+use Drupal\Core\Render\ElementInfoManager;
+use Drupal\inline_form_errors\SupportedRenderElementHelper;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\inline_form_errors\SupportedRenderElementHelper
+ * @group InlineFormErrors
+ */
+class SupportedRenderElementHelperTest extends UnitTestCase {
+
+  /**
+   * @covers ::supportsInlineFormErrors
+   * @covers ::isFormElement
+   *
+   * @dataProvider providerTestSupportsInlineFormErrors
+   *
+   * @param string $element_type
+   *   The name of the element type.
+   * @param array $definition
+   *   The element type definition.
+   * @param bool $expected
+   *   Whether the element type is expected to support Inline Form Errors.
+   */
+  public function testSupportsInlineFormErrors($element_type, array $definition, $expected) {
+    $element_info_manager = $this->prophesize(ElementInfoManager::class);
+    if ($definition) {
+      $element_info_manager->hasDefinition($element_type)->willReturn(TRUE);
+      $element_info_manager->getDefinition($element_type)->willReturn($definition);
+    }
+    else {
+      $element_info_manager->hasDefinition($element_type)->willReturn(FALSE);
+    }
+
+    $supported_render_element_helper = new SupportedRenderElementHelper($element_info_manager->reveal());
+
+    $this->assertSame($expected, $supported_render_element_helper->supportsInlineFormErrors($element_type));
+  }
+
+  /**
+   * Provides test data for ::testSupportsInlineFormErrors().
+   */
+  public function providerTestSupportsInlineFormErrors() {
+    $data = [];
+    // Test that an element implementing FormElementInterface is detected.
+    $data['form_element_type'] = [
+      'form_element_type',
+      ['class' => FormElement::class],
+      TRUE,
+    ];
+    // Test that an element implementing only ElementInterface returns FALSE.
+    $data['render_element_type'] = [
+      'render_element_type',
+      ['class' => RenderElement::class],
+      FALSE,
+    ];
+    // Test that any element marked as supported by inline form errors is
+    // detected. The 'details' element is one of the types specified in
+    // \Drupal\inline_form_errors\SupportedRenderElementHelper::$pseudoFormElements.
+    $data['details'] = [
+      'details',
+      [],
+      TRUE,
+    ];
+    return $data;
+  }
+
+}
diff --git a/core/modules/quickedit/src/Form/QuickEditFieldForm.php b/core/modules/quickedit/src/Form/QuickEditFieldForm.php
index ba2dac3..7603159 100644
--- a/core/modules/quickedit/src/Form/QuickEditFieldForm.php
+++ b/core/modules/quickedit/src/Form/QuickEditFieldForm.php
@@ -114,6 +114,10 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
       '#attributes' => ['class' => ['quickedit-form-submit']],
     ];
 
+    // Use the non-inline form error display for Quick Edit forms, because in
+    // this case the errors are already near the form element.
+    $form['#disable_inline_form_errors'] = TRUE;
+
     // Simplify it for optimal in-place use.
     $this->simplify($form, $form_state);
 
